package day32.stream;

import java.util.ArrayList;
import java.util.Arrays;
import java.util.Comparator;
import java.util.List;
import java.util.Map;
import java.util.Optional;
import java.util.Set;
import java.util.function.BinaryOperator;
import java.util.function.Function;
import java.util.stream.Collectors;
import java.util.stream.Stream;

import day32.stream.Emp.State;

// Stream的终止操作
public class Demo03 {
	public static void main(String[] args) {
		test05();
	}

	// 多级分组收集
	private static void test05() {
		// 按照员工状态分组
		// 每组员工再按照年龄段分组

		Function<Emp, String> fun = e -> {
			if (e.getAge() <= 30)
				return "青年";
			if (e.getAge() <= 40)
				return "中年";
			return "老年";
		};

		Map<State, Map<String, List<Emp>>> map = Emp.getTestData().stream()
				.collect(Collectors.groupingBy(Emp::getState, Collectors.groupingBy(fun)));
		System.out.println(map);
		/*
		 {
			 FREE={
			 	青年=[Emp [name=张三, age=28, salary=2300.0, state=FREE], Emp [name=jones, age=20, salary=2975.0, state=FREE], Emp [name=clark, age=27, salary=2450.0, state=FREE], Emp [name=allen, age=30, salary=1600.0, state=FREE], Emp [name=martin, age=29, salary=1250.0, state=FREE], Emp [name=turner, age=26, salary=1500.0, state=FREE]], 
			 	中年=[Emp [name=miller, age=33, salary=1300.0, state=FREE]]
			 }, 
			 BUSY={
			 	青年=[Emp [name=李富贵, age=29, salary=2450.0, state=BUSY], Emp [name=李二狗, age=30, salary=2350.0, state=BUSY], Emp [name=king, age=28, salary=5000.0, state=BUSY], Emp [name=scott, age=22, salary=3000.0, state=BUSY], Emp [name=ford, age=26, salary=3000.0, state=BUSY], Emp [name=smith, age=21, salary=800.0, state=BUSY], Emp [name=smith, age=21, salary=800.0, state=BUSY], Emp [name=smith, age=21, salary=800.0, state=BUSY]], 
			 	中年=[Emp [name=李狗蛋, age=37, salary=2600.0, state=BUSY], Emp [name=adams, age=35, salary=1100.0, state=BUSY]]
			 }, 
			 VOCATION={
			 	青年=[Emp [name=blake, age=24, salary=2850.0, state=VOCATION], Emp [name=james, age=28, salary=950.0, state=VOCATION]], 
			 	中年=[Emp [name=刘大胆, age=40, salary=2300.0, state=VOCATION], Emp [name=ward, age=32, salary=1250.0, state=VOCATION]]
			 }
		 }
		 */
	}

	// 分组收集
	private static void test04() {
		// 按照员工状态分组, 分组结果是个Map
		// map的key是员工的状态
		// map的values是某个状态员工的集合
		Map<State, List<Emp>> map = Emp.getTestData().stream()
				.collect(Collectors.groupingBy(Emp::getState));
		System.out.println(map);

		/*
		 {
		 BUSY=[Emp [name=李富贵, age=29, salary=2450.0, state=BUSY], Emp [name=李狗蛋, age=37, salary=2600.0, state=BUSY], Emp [name=李二狗, age=30, salary=2350.0, state=BUSY], Emp [name=king, age=28, salary=5000.0, state=BUSY], Emp [name=scott, age=22, salary=3000.0, state=BUSY], Emp [name=ford, age=26, salary=3000.0, state=BUSY], Emp [name=adams, age=35, salary=1100.0, state=BUSY], Emp [name=smith, age=21, salary=800.0, state=BUSY], Emp [name=smith, age=21, salary=800.0, state=BUSY], Emp [name=smith, age=21, salary=800.0, state=BUSY]], 
		 FREE=[Emp [name=张三, age=28, salary=2300.0, state=FREE], Emp [name=jones, age=20, salary=2975.0, state=FREE], Emp [name=clark, age=27, salary=2450.0, state=FREE], Emp [name=allen, age=30, salary=1600.0, state=FREE], Emp [name=martin, age=29, salary=1250.0, state=FREE], Emp [name=turner, age=26, salary=1500.0, state=FREE], Emp [name=miller, age=33, salary=1300.0, state=FREE]], 
		 VOCATION=[Emp [name=刘大胆, age=40, salary=2300.0, state=VOCATION], Emp [name=blake, age=24, salary=2850.0, state=VOCATION], Emp [name=ward, age=32, salary=1250.0, state=VOCATION], Emp [name=james, age=28, salary=950.0, state=VOCATION]]
		 }
		 */
	}

	// 收集
	// collect(Collector col)
	// 将流转换为其它形式, 根据收集器Collector的实现, 对流中的元素进行汇总
	// 收集器Collector中的方法决定了对流中的元素如何收集
	// 如: 可以将元素收集到List中或Set中或Map中等
	// Collectors工具类, 提供了很多静态方法, 获取常用收集器实现
	private static void test03() {
		// 将所有员工的姓名存入到一个List中
		List<String> list = Emp.getTestData().stream().distinct().map(Emp::getName)
				.collect(Collectors.toList());

		// 将所有员工的姓名存入到一个Set中
		Set<String> set = Emp.getTestData().stream().distinct().map(Emp::getName)
				.collect(Collectors.toSet());

		// 将员工姓名收集到已预创建好的List中
		List<String> names = new ArrayList<String>();
		// Supplier<List<String>> sup = () -> names;
		Emp.getTestData().stream().distinct().map(Emp::getName)
				.collect(Collectors.toCollection(() -> names));
	}

	// 归约
	// 将流中的元素反复地进行结合, 最终得到一个值
	// reduce(T t, BinaryOperator bo)
	// reduce(BinaryOperator bo)
	private static void test02() {
		// 计算 1 + 2 + 3 + 4 + ... + 10
		Stream<Integer> stream = Arrays.stream(new Integer[] { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 });
		// BinaryOperator, 二元运算
		// T apply(T t1, T t2);
		BinaryOperator<Integer> bo = (t1, t2) -> t1 + t2;
		// 原理:
		// 从流中取第一个元素, 作为t1
		// 从流中取第二个元素, 作为t2
		// 根据apply定义的操作得到结果
		// 将结果再作为t1, 从流中取第三个元素作为t2
		// 根据apply定义的操作得到结果
		// 将结果再作为t1, 从流中取第四个元素作为t2
		// 根据apply定义的操作得到结果
		// 以此类推, 直到将流中的元素都操作完
		Optional<Integer> op = stream.reduce(bo);
		// op.ifPresent(System.out::println);

		Stream<Integer> stream2 = Arrays.stream(new Integer[] { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 });
		// 原理:
		// 将reduce的第一个参数t, 作为二元运算的t1
		// 从流中取第一个元素, 作为t2
		// 根据apply定义的操作得到结果
		// 将结果再作为t1, 从流中取第二个元素作为t2
		// 根据apply定义的操作得到结果
		// 将结果再作为t1, 从流中取第三个元素作为t2
		// 根据apply定义的操作得到结果
		// 以此类推, 直到将流中的元素都操作完
		Integer sum = stream2.reduce(0, bo);
		// System.out.println(sum);

		// 计算所有员工的工资总和
		Double s = Emp.getTestData().stream().distinct().map(Emp::getSalary).reduce(0D,
				Double::sum);
		System.out.println(s);
		// map与reduce结合使用, 称为`map-reduce`模式

	}

	// 查找与匹配
	private static void test01() {
		// allMatch(Predicate p), 是否所有元素都满足条件
		// 是不是所有的员工都在忙
		boolean flag = Emp.getTestData().stream()
				.allMatch(e -> e.getState().equals(Emp.State.BUSY));
		System.out.println(flag);

		// anyMatch(Predicate p), 至少有一个元素匹配
		// 是不是至少有一个员工空闲
		flag = Emp.getTestData().stream().anyMatch(e -> e.getState().equals(Emp.State.FREE));
		System.out.println(flag);

		// noneMatch(Predicate p), 是不是所有元素都不匹配
		// 是不是没有人在休假
		flag = Emp.getTestData().stream().noneMatch(e -> e.getState().equals(Emp.State.VOCATION));
		System.out.println(flag);

		// findFirst(), 返回第一个元素
		Optional<Emp> op = Emp.getTestData().stream().findFirst();
		// op.ifPresent(System.out::println);

		// findAny(), 返回任意一个元素
		// 从所有空闲员工中随便找个人
		op = Emp.getTestData().parallelStream().filter(e -> e.getState().equals(Emp.State.FREE))
				.findAny();
		//op.ifPresent(System.out::println);
		// findAny操作, 用串行流时, 效果并非是随机获取
		// 应当结合并行流使用

		// count(), 获取流中元素的数量
		long count = Emp.getTestData().stream().count();
		// System.out.println(count);

		// 有多少个员工是空闲的
		count = Emp.getTestData().stream().filter(e -> e.getState().equals(Emp.State.FREE)).count();
		// System.out.println(count);

		// max(Comparator com), 根据比较器规则获取流中最大元素
		// min(Comparator com), 根据比较器规则获取流中最小元素
		// max与min查找时, 比较器规则应当尽量为正序规则
		Comparator<Emp> com = (e1, e2) -> e1.getSalary().compareTo(e2.getSalary());
		op = Emp.getTestData().stream().max(com);
		op.ifPresent(System.out::println);
		op = Emp.getTestData().stream().min(com);
		op.ifPresent(System.out::println);
	}
}
